package huanzhao.erp.service;

import cn.dev33.satoken.stp.StpUtil;
import huanzhao.erp.domain.entity.Staff;
import huanzhao.erp.domain.exception.StaffNotFoundException;
import huanzhao.erp.domain.po.PermissionCombine;
import huanzhao.erp.domain.po.Scope;
import huanzhao.erp.domain.types.DepartmentType;
import huanzhao.erp.domain.types.PermissionTarget;
import huanzhao.erp.domain.types.PermissionType;
import huanzhao.erp.mapper.PositionMapper;
import huanzhao.erp.mapper.StaffMapper;
import huanzhao.erp.mapper.TeamMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotNull;
import java.util.*;

/**
 * @author April
 * 2024/10/5 09:54
 * @version 1.0
 */
@Service
@Validated
public class PermissionService {

    private final StaffMapper staffMapper;
    private final PositionMapper positionMapper;
    private final TeamMapper teamMapper;

    public PermissionService(StaffMapper staffMapper, PositionMapper positionMapper, TeamMapper teamMapper) {
        this.staffMapper = staffMapper;
        this.positionMapper = positionMapper;
        this.teamMapper = teamMapper;
    }

    /**
     * 鉴定当前登录用户是否对目标员工指定权限
     * 目标员工不存在或无权限直接抛出异常
     *
     * @param staffId 目标员工id
     * @param type    权限类型
     *                2024/10/4 17:30:41
     */
    public void checkPermissionToStaff(Long staffId, PermissionType type) {
        Staff staff = staffMapper.selectById(staffId);
        if (staff == null) {
            throw new StaffNotFoundException("员工不存在");
        }
        // 先产生需要的权限列表，只要满足其中一个即可
        List<String> requiredPermissions = new ArrayList<>(List.of("company:staff:" + type));
        // 首先检查对员工所属的部门的权限
        PermissionCombine combine = new PermissionCombine(PermissionTarget.STAFF, type);
        if (staff.getMaintainer()) {
            requiredPermissions.add(getPermissionToDept(DepartmentType.REPAIR, combine));
        }
        if (staff.getPurchaser()) {
            requiredPermissions.add(getPermissionToDept(DepartmentType.PURCHASE, combine));
        }
        if (staff.getWarehouser()) {
            requiredPermissions.add(getPermissionToDept(DepartmentType.WAREHOUSE, combine));
        }
        // 检查对员工所属的团队的权限
        List<Long> teamIds = positionMapper.getStaffBelongTeamIds(staff.getId());
        for (Long teamId : teamIds) {
            requiredPermissions.add(getPermissionToTeam(teamId, combine));
        }
        StpUtil.checkPermissionOr(requiredPermissions.toArray(new String[0]));
    }



    /**
     * 鉴定当前登录用户是否对目标部门指定权限
     *
     * @param dept   部门
     * @param target 操作对象：只能为员工、手机、任务、统计数据
     * @param type   操作类型
     *               2024/10/12 13:11:46
     */
    public void checkPermissionToDept(@NotNull DepartmentType dept, @NotNull PermissionTarget target, @NotNull PermissionType type) {
        StpUtil.checkPermission(getPermissionToDept(dept, target, type));
    }

    public void checkPermissionToDept(@NotNull DepartmentType dept,@NotNull PermissionCombine combine) {
        StpUtil.checkPermission(getPermissionToDept(dept, combine.target(), combine.type()));
    }

    /**
     * 鉴定当前登录用户是否对目标团队指定权限
     *
     * @param teamId 团队id
     * @param target 操作对象：只能为员工、手机、任务、统计数据
     * @param type   操作类型
     *               2024/10/12 13:14:03
     */
    public void checkPermissionToTeam(@NotNull Long teamId, @NotNull PermissionTarget target, @NotNull PermissionType type) {
        StpUtil.checkPermissionOr(getPermissionToDept(DepartmentType.QC, target, type), getPermissionToTeam(teamId, target, type));
    }

    public void checkPermissionToTeam(@NotNull Long teamId,@NotNull PermissionCombine combine) {
        StpUtil.checkPermissionOr(getPermissionToDept(DepartmentType.QC, combine.target(),
                combine.type()), getPermissionToTeam(teamId, combine.target(), combine.type()));
    }

    /**
     * 获取对部门权限的字符串表示
     *
     * @param dept   部门
     * @param target 操作对象：只能为员工、手机、任务、统计数据
     * @param type   操作类型
     * @return String
     * 2024/10/12 13:16:56
     */
    private String getPermissionToDept(@NotNull DepartmentType dept, @NotNull PermissionTarget target, @NotNull PermissionType type) {
        return String.join(":", "dept", dept.name().toLowerCase(), target.name().toLowerCase(), type.name().toLowerCase());
    }

    private String getPermissionToDept(DepartmentType dept, PermissionCombine combine) {
        return getPermissionToDept(dept, combine.target(), combine.type());
    }

    /**
     * 获取对团队权限的字符串表示
     *
     * @param teamId 团队id
     * @param target 操作对象：只能为员工、手机、任务、统计数据
     * @param type   操作类型
     * @return String
     * 2024/10/12 13:18:00
     */
    private String getPermissionToTeam(@NotNull Long teamId, @NotNull PermissionTarget target, @NotNull PermissionType type) {
        return String.join(":", "team", teamId.toString(), target.name().toLowerCase(), type.name().toLowerCase());
    }

    private String getPermissionToTeam(Long teamId, PermissionCombine combine) {
        return getPermissionToTeam(teamId, combine.target(), combine.type());
    }

    /**
     * 获取当前员工有权对部门中的对象进行特定操作的部门集合
     *
     * @param target 操作对象：员工、手机、任务等
     * @param type   操作类型：增删改查等
     * @return Set<DepartmentType>
     * 2024/10/5 10:17:16
     */
    public Set<DepartmentType> getAuthorizedDepartments(@NotNull PermissionTarget target, @NotNull PermissionType type) {
        Set<DepartmentType> viewableDepartments = new HashSet<>();
        for (DepartmentType departmentType : DepartmentType.values()) {
            String permission = String.join(":", "dept", departmentType.name().toLowerCase(), target.name().toLowerCase(), type.name().toLowerCase());
            if (StpUtil.hasPermission(permission)) {
                viewableDepartments.add(departmentType);
            }
        }
        return viewableDepartments;
    }

    /**
     * 获取当前员工有权对团队中的对象进行特定操作的团队集合
     *
     * @param target 操作对象：员工、手机、任务等
     * @param type   操作类型：增删改查等
     * @return Set<Long>
     * 2024/10/5 10:23:51
     */
    public Set<Long> getAuthorizedTeams(@NotNull PermissionTarget target, @NotNull PermissionType type) {
        List<Long> teamIds = teamMapper.getAllTeamIds();
        if (teamIds == null) {
            return Collections.emptySet();
        }
        Set<Long> viewableTeams = new HashSet<>();
        teamIds.forEach(teamId -> {
            String permission = String.join(":", "team", teamId.toString(), target.name().toLowerCase(), type.name().toLowerCase());
            if (StpUtil.hasPermission(permission)) {
                viewableTeams.add(teamId);
            }
        });
        return viewableTeams;
    }

    /**
     * 根据指定的部门和团队结合员工自身的权限，获取其可以操作的部门、团队范围
     *
     * @param departmentCode 部门代码
     * @param teamId         团队id
     * @param target         操作目标
     * @param type           操作类型
     * @return Scope
     * 2024/10/7 16:17:25
     */
    public Scope getConditionalAuthorizedScope(Integer departmentCode, Long teamId, @NotNull PermissionTarget target, @NotNull PermissionType type) {
        // 能看的部门
        Set<DepartmentType> viewableDept = getAuthorizedDepartments(target, type);
        Set<Long> viewableTeams = null;
        // 当指定了查询的部门时
        if (departmentCode != null) {
            DepartmentType departmentType = DepartmentType.of(departmentCode);
            // 若查询的部门不是qc且能看的部门中没有该部门，返回空
            if (departmentType != DepartmentType.QC) {
                if (!viewableDept.contains(departmentType)) {
                    return Scope.disable();
                }
            } else {
                // 查询的部门为qc的情况
                viewableTeams = getAuthorizedTeams(target, type);
                // 若能看的团队中没有该团队，返回空
                if (teamId != null) {
                    if (!viewableTeams.contains(teamId)) {
                        return Scope.disable();
                    }
                    viewableTeams = Collections.singleton(teamId);
                } else {
                    // 若未指定查询的团队，说明想要看到质检团队的所有员工
                    if (!viewableDept.contains(DepartmentType.QC)) {
                        return Scope.disable();
                    }
                }
            }
            viewableDept = Collections.singleton(departmentType);
        } else {
            // 若未指定查询的部门，则可以看到其能看的所有团队的员工
            viewableTeams = getAuthorizedTeams(target, type);
        }
        return new Scope(false, viewableDept.contains(DepartmentType.PURCHASE), viewableDept.contains(DepartmentType.WAREHOUSE), viewableDept.contains(DepartmentType.REPAIR), viewableTeams);
    }
}
